package oa.hleast.xswl.service.place.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import oa.hleast.common.constant.CacheConstant;
import oa.hleast.common.web.exception.BizException;
import oa.hleast.xswl.common.constant.CommonConstant;
import oa.hleast.xswl.mapper.place.PlaceTypeMapper;
import oa.hleast.xswl.pojo.domain.place.CmsPlaceType;
import oa.hleast.xswl.pojo.form.place.CmsPlaceTypeForm;
import oa.hleast.xswl.pojo.vo.place.CmsPlaceTypeVo;
import oa.hleast.xswl.pojo.vo.place.ParentPlaceTypeVo;
import oa.hleast.xswl.pojo.vo.place.ShowPlaceTypeVo;
import oa.hleast.xswl.service.place.IPlaceTypeService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author 302
 * @description 地图类型相关接口
 * @date 2021/3/12
 */

@Transactional
@Slf4j
@Service
@AllArgsConstructor
public class PlaceTypeServiceImpl extends ServiceImpl<PlaceTypeMapper, CmsPlaceType> implements IPlaceTypeService {

    /**
     * 地图分类列表
     */
    @Override
    public List<ShowPlaceTypeVo> getShowPlaceTypeList(Long id) {
        LambdaQueryWrapper<CmsPlaceType> queryWrapper = new LambdaQueryWrapper<CmsPlaceType>()
                .eq(CmsPlaceType::getDisplay,1)
                .orderByAsc(CmsPlaceType::getSort);

        List<CmsPlaceType> placeTypeList = this.list(queryWrapper);
        List<ShowPlaceTypeVo> list = recursionShowForCascaderList(id, placeTypeList);
        log.info("list = {}", list);

        return list;
    }

    /**
     * 地图分类列表
     */
    @Override
    public List<CmsPlaceTypeVo> finsShowPlaceTypeOnlyLast(Long parentsTypeId) {
        LambdaQueryWrapper<CmsPlaceType> queryWrapper = new LambdaQueryWrapper<CmsPlaceType>()
                .eq(CmsPlaceType::getParentsTypeId,parentsTypeId)
                .orderByAsc(CmsPlaceType::getSort);

        List<CmsPlaceType> placeTypeList = this.list(queryWrapper);
//        List<ShowPlaceTypeVo> list = recursionShowForCascaderList(id, placeTypeList);
        List<CmsPlaceTypeVo> voList =  placeTypeList.stream().map(this::conversionToVo).collect(Collectors.toList());

        log.info("list = {}", voList);

        return voList;
    }

    /**
     * 地图父级分类列表
     */
    @Override
    public List<ParentPlaceTypeVo> getParent() {

        LambdaQueryWrapper<CmsPlaceType> queryWrapper = new LambdaQueryWrapper<CmsPlaceType>()
                .eq(CmsPlaceType::getDeleted, 0)
                .between(CmsPlaceType::getTypeLevel, 0, CommonConstant.MIX_PARENTS_PLACE_TYPE_LEVEL)
                .orderByAsc(CmsPlaceType::getSort)
                .orderByDesc(CmsPlaceType::getGmtModified)
                .orderByDesc(CmsPlaceType::getGmtCreate);
        List<CmsPlaceType> list = this.list(queryWrapper);
        List<ParentPlaceTypeVo> parentPlaceTypeVoList = conversionToHomePageVoList(list);
        log.info("parentPlaceTypeVoList = {} ", parentPlaceTypeVoList);

        return parentPlaceTypeVoList;
    }

    @Override
    public List<CmsPlaceTypeVo> cascaderPlaceTypeList(String typeName) {

        // 获取所有数据
        List<CmsPlaceType> placeTypeList = this.list(new LambdaQueryWrapper<CmsPlaceType>()
                .orderByAsc(CmsPlaceType::getSort));

        // 条件匹配数据
        CmsPlaceType placeType = this.getOne(new LambdaQueryWrapper<CmsPlaceType>().eq(CmsPlaceType::getTypeName, typeName), false);
        Long parentId = ObjectUtils.isEmpty(placeType) ? 0L : placeType.getId();

        // 递归获取树状结构数据
        List<CmsPlaceTypeVo> list = recursionForCascaderList(parentId, placeTypeList);
        return list;
    }


    @Override
    public Long add(CmsPlaceTypeForm cmsPlaceTypeForm) {

        CmsPlaceType cmsPlaceType = conversionToPojo(cmsPlaceTypeForm);

        Long parentsTypeId = cmsPlaceTypeForm.getParentsTypeId();

        // 获取层级
        int typeLevel = getThisTypeLevel(parentsTypeId);
        cmsPlaceType.setTypeLevel(typeLevel);

        //TODO 后期考虑如果一级分类包含内容，是否可以添加二级分类

        this.save(cmsPlaceType);

        return cmsPlaceType.getId();
    }

    @Override
    public Page<CmsPlaceTypeVo> pageList(Integer page, Integer limit, String typeName, Integer display, Integer typeLevel) {

        LambdaQueryWrapper<CmsPlaceType> queryWrapper = new LambdaQueryWrapper<CmsPlaceType>()
                .orderByAsc(CmsPlaceType::getSort)
                .orderByDesc(CmsPlaceType::getGmtModified)
                .orderByDesc(CmsPlaceType::getGmtCreate);

        if (ObjectUtils.isNotEmpty(display)) {
            queryWrapper.eq(CmsPlaceType::getDisplay, display);
        }
        if (ObjectUtils.isNotEmpty(typeName)) {
            queryWrapper.like(CmsPlaceType::getTypeName, typeName);
        }
        if (ObjectUtils.isNotEmpty(typeLevel)) {
            queryWrapper.eq(CmsPlaceType::getTypeLevel, typeLevel);
        }

        Page<CmsPlaceType> cmsPlaceTypePage = this.page(new Page<>(page, limit), queryWrapper);

        if (cmsPlaceTypePage.getTotal() == 0) {
            log.info("cmsPlaceTypePage 返回空参数");
            return new Page<>(page, limit);
        }

        List<CmsPlaceTypeVo> cmsPlaceTypeVoList = cmsPlaceTypePage.getRecords().stream().map(this::conversionToVo).collect(Collectors.toList());

        Page<CmsPlaceTypeVo> cmsAppsIconVoPage = new Page<>(page, limit, cmsPlaceTypePage.getTotal());
        cmsAppsIconVoPage.setRecords(cmsPlaceTypeVoList);

        log.info("cmsAppsIconVoPage ={}", cmsAppsIconVoPage.toString());
        return cmsAppsIconVoPage;
    }

    @Override
    public Long update(CmsPlaceTypeForm cmsPlaceTypeForm) {

        // 所有数据
        List<CmsPlaceType> placeTypeList = this.list();

        // 1，校验ID
        CmsPlaceType cmsAppsIconOld = this.getById(cmsPlaceTypeForm.getId());
        if (ObjectUtils.isEmpty(cmsAppsIconOld)) {
            throw new BizException("没有找到该数据！");
        }

        Long parentsTypeId = cmsPlaceTypeForm.getParentsTypeId();
        // 2，校验 更新的父节点不能是以前的子节点
        List<CmsPlaceType> sonList0 = new ArrayList<>();
        recursionDataForCascaderList(sonList0, cmsPlaceTypeForm.getId(), placeTypeList);
        for (CmsPlaceType placeType : sonList0) {
            if (placeType.getId().equals(parentsTypeId)) {
                throw new BizException("该操作不合法");
            }
        }

        CmsPlaceType cmsPlaceType = new CmsPlaceType();
        BeanUtil.copyProperties(cmsPlaceTypeForm, cmsPlaceType);
        cmsPlaceType.setId(cmsAppsIconOld.getId());

        // 3， 获取层级
        int typeLevel = getThisTypeLevel(parentsTypeId);
        cmsPlaceType.setTypeLevel(typeLevel);

        // 4，更新此节点数据
        boolean update = this.updateById(cmsPlaceType);

        // 5， 更新该节点下的所有根节点与叶子节点的层级
        // 5.1 获取此节点跃迁的层级差值
        int difference = cmsPlaceType.getTypeLevel() - cmsAppsIconOld.getTypeLevel();

        // 5.2 这里使用值传递的方式 ，递归获取所有的此节点下的根节点与叶子节点的集合
        List<CmsPlaceType> sonList = new ArrayList<>();
        recursionIdForCascaderList(sonList, difference, cmsPlaceTypeForm.getId(), placeTypeList);
        if (CollectionUtils.isNotEmpty(sonList)) {
            update = update & this.updateBatchById(sonList);
        }

        return cmsPlaceTypeForm.getId();
    }

    @Override
    @CacheEvict(value = CacheConstant.MAP_PLACE_TYPE_KEY, allEntries = true)
    public boolean deleteByIds(List<String> asList) {
        if (CollectionUtils.isFull(asList)) {
            throw new BizException("没有选择任何数据，请重新操作！");
        }

        return this.removeByIds(asList);
    }

    @Override
    public CmsPlaceTypeVo detail(Long id) {

        CmsPlaceType cmsPlaceType = this.getById(id);

        CmsPlaceTypeVo cmsPlaceTypeVo = conversionToVo(cmsPlaceType);

        return cmsPlaceTypeVo;
    }


    /**
     * ===========  私有方法 ===========
     */
    private CmsPlaceTypeVo conversionToVo(CmsPlaceType cmsPlaceType) {
        if (ObjectUtils.isEmpty(cmsPlaceType)) {
            log.info("conversionToVo 方法返回空参数");
            return new CmsPlaceTypeVo();
        }

        CmsPlaceTypeVo cmsAppsIconVo = new CmsPlaceTypeVo();
        BeanUtil.copyProperties(cmsPlaceType, cmsAppsIconVo);

        log.info("cmsAppsIconVo = {}", cmsAppsIconVo);

        //添加父级的分类名
        if (ObjectUtils.isNotEmpty(cmsPlaceType.getTypeLevel()) && cmsPlaceType.getTypeLevel() == 2 && ObjectUtils.isNotEmpty(cmsPlaceType.getParentsTypeId())) {
            CmsPlaceType placeType = this.getById(cmsPlaceType.getParentsTypeId());
            if (ObjectUtils.isNotEmpty(placeType)) {
                cmsAppsIconVo.setParentsTypeName(placeType.getTypeName());
            }
        }

        log.info("cmsAppsIconVo = {}", cmsAppsIconVo);

        return cmsAppsIconVo;
    }

    private ShowPlaceTypeVo conversionToShowVo(CmsPlaceType cmsPlaceType) {
        if (ObjectUtils.isEmpty(cmsPlaceType)) {
            return null;
        }

        ShowPlaceTypeVo showPlaceTypeVo = new ShowPlaceTypeVo();
        BeanUtil.copyProperties(cmsPlaceType, showPlaceTypeVo);

        return showPlaceTypeVo;
    }

    private CmsPlaceType conversionToPojo(CmsPlaceTypeForm appsIconForm) {
        if (ObjectUtils.isEmpty(appsIconForm)) {
            throw new BizException("提交的数据表单为空");
        }

        CmsPlaceType cmsPlaceType = new CmsPlaceType();
        BeanUtil.copyProperties(appsIconForm, cmsPlaceType);

        return cmsPlaceType;
    }

    private List<ParentPlaceTypeVo> conversionToHomePageVoList(List<CmsPlaceType> cmsPlaceTypeList) {
        if (CollectionUtils.isEmpty(cmsPlaceTypeList)) {
            return new ArrayList<>();
        }
        return cmsPlaceTypeList.stream().map(this::conversionToParentVo).collect(Collectors.toList());
    }

    private ParentPlaceTypeVo conversionToParentVo(CmsPlaceType cmsPlaceType) {
        ParentPlaceTypeVo parentPlaceTypeVo = new ParentPlaceTypeVo();
        BeanUtil.copyProperties(cmsPlaceType, parentPlaceTypeVo);
        return parentPlaceTypeVo;
    }

    /**
     * 获取类型的层级
     *
     * @param parentsTypeId 父类型ID
     */
    private int getThisTypeLevel(Long parentsTypeId) {

        int typeLevel = 1;
        //  1 判断有没有父类型ID或者大于0，true继续进行 ，false 则层级为1
        if (ObjectUtils.isNotEmpty(parentsTypeId) && parentsTypeId > 0) {
            CmsPlaceType parentsPlaceType = this.getById(parentsTypeId);

            if (ObjectUtils.isEmpty(parentsPlaceType) || ObjectUtils.isEmpty(parentsPlaceType.getTypeLevel())) {
                throw new BizException("该父层级不可用！");
            }

            //  2 根据父类型ID获取父类型层级，如果父层级大于阀值，抛出异常
            if (parentsPlaceType.getTypeLevel() > CommonConstant.MIX_PARENTS_PLACE_TYPE_LEVEL) {
                throw new BizException("只能设置" + (CommonConstant.MIX_PLACE_TYPE_LEVEL) + "级层级！");
            } else {
                //  3 父层级在阀值内 ，则层级为父层级+1
                typeLevel = parentsPlaceType.getTypeLevel() + 1;
            }
        }

        return typeLevel;
    }

    /**
     * 递归 获取指定节点下的所有根节点与叶子节点
     * dataList 为真正需要的数据
     *
     * @param dataList      值传递 的数据存储
     * @param parentId      父类ID
     * @param placeTypeList 数据集
     * @return
     */
    private List<CmsPlaceTypeVo> recursionDataForCascaderList(List<CmsPlaceType> dataList, Long parentId, List<CmsPlaceType> placeTypeList) {

        List<CmsPlaceTypeVo> list = new ArrayList<>();
        Optional.ofNullable(placeTypeList)
                .ifPresent(categories ->
                        categories.stream().filter(category ->
                                category.getParentsTypeId().equals(parentId))
                                .forEach(category -> {
                                    CmsPlaceTypeVo vo = conversionToVo(category);
                                    List<CmsPlaceTypeVo> children = recursionDataForCascaderList(dataList, category.getId(), placeTypeList);
                                    vo.setSonPlaceTypeList(children);

                                    //收集列表数据
                                    dataList.add(category);
                                    list.add(vo);
                                })
                );
        return list;
    }

    /**
     * 递归 获取指定节点下的所有根节点与叶子节点的层级更新
     * dataList 为真正需要的数据
     *
     * @param difference    变化的层级的差值
     * @param dataList      值传递 的数据存储
     * @param parentId      父类ID
     * @param placeTypeList 数据集
     * @return
     */
    private List<CmsPlaceTypeVo> recursionIdForCascaderList(List<CmsPlaceType> dataList, int difference, Long parentId, List<CmsPlaceType> placeTypeList) {

        List<CmsPlaceTypeVo> list = new ArrayList<>();
        Optional.ofNullable(placeTypeList)
                .ifPresent(categories ->
                        categories.stream().filter(category ->
                                category.getParentsTypeId().equals(parentId))
                                .forEach(category -> {
                                    CmsPlaceTypeVo vo = conversionToVo(category);
                                    List<CmsPlaceTypeVo> children = recursionIdForCascaderList(dataList, difference, category.getId(), placeTypeList);
                                    vo.setSonPlaceTypeList(children);

                                    int level = category.getTypeLevel() + difference;
                                    if (level <= 0) {
                                        throw new BizException("此数据下的层级小于1级");
                                    }
                                    if (level > CommonConstant.MIX_PLACE_TYPE_LEVEL) {
                                        throw new BizException("此数据下的层级超过最大层级数" + CommonConstant.MIX_PLACE_TYPE_LEVEL);
                                    }
                                    //收集列表数据
                                    dataList.add(CmsPlaceType.builder()
                                            .id(category.getId())
                                            .typeLevel(level)
                                            .build());
                                    list.add(vo);
                                })
                );
        return list;
    }

    /**
     * 递归 获取指定节点下的所有根节点与叶子节点的 树状结构数据  CmsPlaceTypeVo
     *
     * @param parentId      父类ID
     * @param placeTypeList 数据集
     * @return 树状结构数据 CmsPlaceTypeVo
     */
    private List<CmsPlaceTypeVo> recursionForCascaderList(Long parentId, List<CmsPlaceType> placeTypeList) {

        List<CmsPlaceTypeVo> list = new ArrayList<>();
        Optional.ofNullable(placeTypeList)
                .ifPresent(categories ->
                        categories.stream().filter(category ->
                                category.getParentsTypeId().equals(parentId))
                                .forEach(category -> {
                                    // 地图的最大允许展示层级
                                    if (category.getTypeLevel() > CommonConstant.MIX_SHOW_PLACE_TYPE_LEVEL) {
                                        return;
                                    }
                                    CmsPlaceTypeVo vo = conversionToVo(category);
                                    List<CmsPlaceTypeVo> children = recursionForCascaderList(category.getId(), placeTypeList);
                                    vo.setSonPlaceTypeList(children);

                                    list.add(vo);
                                })
                );
        return list;
    }

    /**
     * 递归 获取指定节点下的所有根节点与叶子节点的 树状结构数据 ShowPlaceTypeVo
     *
     * @param parentId      父类ID
     * @param placeTypeList 数据集
     * @return 树状结构数据 ShowPlaceTypeVo
     */
    private List<ShowPlaceTypeVo> recursionShowForCascaderList(Long parentId, List<CmsPlaceType> placeTypeList) {
        List<ShowPlaceTypeVo> list = new ArrayList<>();
        Optional.ofNullable(placeTypeList)
                .ifPresent(categories ->
                        categories.stream().filter(category ->
                                category.getParentsTypeId().equals(parentId))
                                .forEach(category -> {
                                    // 地图的最大允许展示层级
                                    if (category.getTypeLevel() > CommonConstant.MIX_SHOW_PLACE_TYPE_LEVEL) {
                                        return;
                                    }
                                    ShowPlaceTypeVo vo = conversionToShowVo(category);
                                    List<ShowPlaceTypeVo> children = recursionShowForCascaderList(category.getId(), placeTypeList);
                                    vo.setSonShowPlaceType(children);

                                    list.add(vo);
                                })
                );
        return list;
    }


}
